package amd64

import (
	"fmt"

	"gitee.com/u-language/u-language/ucom/ast"
	"gitee.com/u-language/u-language/ucom/internal/utils"
	"gitee.com/u-language/u-language/ucom/ir"
)

func ParserNode(to *AstToAsm, nodes []ast.Node, sbt *ast.Sbt, stack *Stack, vreg *VReg, IsFunc bool, Thread bool) [][]TextNode {
	nlen := len(nodes)
	var retline = make([][]TextNode, nlen)
	wg := utils.NewWaitGroup(Thread)
	for i := 0; i < nlen; i++ {
		switch n := nodes[i].(type) {
		case *ast.VarNode:
			wg.GoI(func(gi int) {
				defer Deferfunc()
				defer wg.Done()
				if !IsFunc {
					data := ParserData(n)
					to.Datas.Add(data)
				} else {
					info := stack.HaveVar(n.Name)
					retline[gi] = append(retline[gi], StackSetVar(to, info.offset, n.Name, n.Value.(*ast.Object).Name, sbt, vreg))
				}
			}, i)
		case *ast.FuncNode:
			wg.GoI(func(gi int) {
				defer Deferfunc()
				defer wg.Done()
				retline[gi] = append(retline[gi], NewLabel(n.Name))
				retline[gi] = append(retline[gi], NewSrcRegDestReg(RBP, RSP, MOV))
				retline[gi] = append(retline[gi], StackAllocText(n.Sbt.Size))
				rets := ParserNode(to, nodes[n.Left:n.Right], n.Sbt, NewStack(n.Sbt), NewVReg(), true, Thread)
				retline[gi] = append(retline[gi], NewFuncKind(rets))
				retline[gi] = append(retline[gi], StackFreeText(n.Sbt.Size))
			}, i)
			i = n.Right + 1
		case *ast.ASSIGNNode:
			wg.GoI(func(gi int) {
				defer Deferfunc()
				defer wg.Done()
				var rets TextNode
				rets = ParserFuncASSIGN(to, n, vreg, sbt, stack)
				retline[gi] = append(retline[gi], rets)
			}, i)
		}
	}
	wg.Wait()
	wg.Close()
	return retline
}

// 转换函数内赋值节点为汇编代码
func ParserFuncASSIGN(to *AstToAsm, node *ast.ASSIGNNode, vreg *VReg, sbt *ast.Sbt, stack *Stack) TextNode {
	var (
		ret    = make([]TextNode, 0, 3)
		tmpreg RegEnum
	)
	irnode := ir.NewToState().ToASSIGN(node, sbt)
	for i := 0; i < len(irnode); i++ {
		switch irnode[i].Op {
		case ir.MOVOP:
			MovIrToAsm(to, irnode[i], sbt, vreg, stack, &ret)
			continue
		case ir.ADDOP, ir.SUBOP:
			bol := Op2IrAutoToAsm(to, irnode[i], &tmpreg, sbt, vreg, stack, &ret, i)
			if bol {
				continue
			}
			switch irnode[i].ResultTyp {
			case ir.Tmp:
				switch irnode[i].Arg1Typ {
				case ir.Var:
					switch irnode[i].Arg2Typ {
					case ir.ImmInt: //op<var,immint,tmp> i==0 ok
						OpVarAndImmIntDestTmpIrToAsm(irnode[i].Arg1Obj, irnode[i].Arg2Obj, &tmpreg, irnode[i].Op, vreg, &ret, i)
						continue
					case ir.ImmFloat: //op<var,immfloat,tmp> i==0 ok
						OpVarAndImmFloatDestTmpIrToAsm(to, irnode[i].Arg1Obj, irnode[i].Arg2Obj, &tmpreg, irnode[i].Op, vreg, &ret, i)
						continue
					case ir.Tmp: //op<var,tmp,tmp> i!=0 ?
						OpTmpAndVarDestTmp(irnode[i].Arg1Obj, &tmpreg, irnode[i].Op, sbt, vreg, &ret)
						continue
					case ir.Var: //op<var,var,tmp> i==0 ok
						OpVarAndVarDestTmp(irnode[i].Arg1Obj, irnode[i].Arg2Obj, &tmpreg, irnode[i].Op, sbt, vreg, &ret, i)
						continue
					}
				case ir.StackVar:
					switch irnode[i].Arg2Typ {
					case ir.Tmp: //op<stackvar,tmp,tmp> i!=0 ?
						OpTmpAndStackVarDestTmp(irnode[i].Arg1Obj, &tmpreg, irnode[i].Op, sbt, stack, vreg, &ret)
						continue
					case ir.ImmInt: //op<stackvar,immint,tmp> i==0 ok
						OpImmIntAndAndStackVarDestTmp(irnode[i].Arg1Obj, irnode[i].Arg2Obj, &tmpreg, irnode[i].Op, sbt, stack, vreg, &ret, i)
						continue
					case ir.ImmFloat: //op<stackvar,immfloat,tmp> i==0 ok
						OpStackVarAndImmFloatDestTmp(to, irnode[i].Arg1Obj, irnode[i].Arg2Obj, &tmpreg, irnode[i].Op, stack, vreg, &ret, i)
						continue
					case ir.StackVar: //op<stackvar,stackvar,tmp>  i==0 ok
						OpStackVarAndStackVarDestTmp(irnode[i].Arg1Obj, irnode[i].Arg2Obj, &tmpreg, irnode[i].Op, sbt, stack, vreg, &ret, i)
						continue
					}
				case ir.Tmp:
					switch irnode[i].Arg2Typ {
					case ir.Var: //op<tmp,var,tmp> i!=0
						OpTmpAndVarDestTmp(irnode[i].Arg2Obj, &tmpreg, irnode[i].Op, sbt, vreg, &ret)
						continue
					case ir.ImmInt: //op<tmp,immint,tmp> i!=0
						OpTmpAndImmIntDestTmp(irnode[i].Arg2Obj, &tmpreg, irnode[i].Op, vreg, &ret)
						continue
					case ir.StackVar: //op<tmp,stackvar,tmp> i!=0
						OpTmpAndStackVarDestTmp(irnode[i].Arg2Obj, &tmpreg, irnode[i].Op, sbt, stack, vreg, &ret)
						continue
					case ir.ImmFloat: //op<tmp,immfloat,tmp> i!=0
						OpTmpAndImmFloatDestTmp(to, irnode[i].Arg2Obj, &tmpreg, irnode[i].Op, vreg, &ret)
						continue
					}
				case ir.ImmInt:
					switch irnode[i].Arg2Typ {
					case ir.Var: //op<immint,var,tmp> i==0 ok
						OpVarAndImmIntDestTmpIrToAsm(irnode[i].Arg2Obj, irnode[i].Arg1Obj, &tmpreg, irnode[i].Op, vreg, &ret, i)
						continue
					case ir.StackVar: //op<immint,stackvar,tmp> i==0 ok
						OpImmIntAndAndStackVarDestTmp(irnode[i].Arg2Obj, irnode[i].Arg1Obj, &tmpreg, irnode[i].Op, sbt, stack, vreg, &ret, i)
						continue
					case ir.Tmp: //op<immint,tmp,tmp> i!=0 ?
						OpTmpAndImmIntDestTmp(irnode[i].Arg2Obj, &tmpreg, irnode[i].Op, vreg, &ret)
						continue
					}
				case ir.ImmFloat:
					switch irnode[i].Arg2Typ {
					case ir.Var: //op<immfloat,var,tmp> i==0 ok
						OpVarAndImmFloatDestTmpIrToAsm(to, irnode[i].Arg2Obj, irnode[i].Arg1Obj, &tmpreg, irnode[i].Op, vreg, &ret, i)
						continue
					case ir.StackVar: //op<immfloat,stackvar,tmp> i==0 ok
						OpStackVarAndImmFloatDestTmp(to, irnode[i].Arg2Obj, irnode[i].Arg1Obj, &tmpreg, irnode[i].Op, stack, vreg, &ret, i)
						continue
					}
				}
			case ir.Var:
				switch irnode[i].Arg1Typ {
				case ir.Tmp:
					switch irnode[i].Arg2Typ {
					case ir.ImmInt: //op<tmp,immint,var> i!=0
						OpTmpAndImmIntDestVarToAsm(irnode[i].ResultObj, irnode[i].Arg2Obj, &tmpreg, irnode[i].Op, vreg, &ret)
						continue
					case ir.ImmFloat: //op<tmp,immfloat,var> i!=0
						OpTmpAndImmFloatDestVarToAsm(to, irnode[i].ResultObj, irnode[i].Arg2Obj, &tmpreg, irnode[i].Op, vreg, &ret)
						continue
					case ir.Var: //op<tmp,var,var> i!=0
						OpTmpAndVarDestVar(irnode[i].Arg2Obj, irnode[i].ResultObj, &tmpreg, irnode[i].Op, sbt, vreg, &ret)
						continue
					}
				case ir.ImmInt:
					switch irnode[i].Arg2Typ {
					case ir.Tmp: //op<immint,tmp,var> i!=0 ?
						OpTmpAndImmIntDestVarToAsm(irnode[i].ResultObj, irnode[i].Arg1Obj, &tmpreg, irnode[i].Op, vreg, &ret)
						continue
					}
				case ir.ImmFloat:
					switch irnode[i].Arg2Typ {
					case ir.Tmp: //op<immfloat,tmp,var> i!=0 ?
						OpTmpAndImmFloatDestVarToAsm(to, irnode[i].ResultObj, irnode[i].Arg1Obj, &tmpreg, irnode[i].Op, vreg, &ret)
						continue
					}
				}
			case ir.StackVar:
				switch irnode[i].Arg1Typ {
				case ir.Tmp:
					switch irnode[i].Arg2Typ {
					case ir.ImmInt: //op<tmp,immint,stackvar> i!=0
						OpTmpAndImmIntDestStackVar(irnode[i].ResultObj, irnode[i].Arg2Obj, &tmpreg, irnode[i].Op, stack, vreg, &ret)
						continue
					case ir.StackVar: //op<tmp,stackvar,stackvar> i!=0 notest
						OpTmpAndStackVarDestStackVar(irnode[i].Arg2Obj, irnode[i].ResultObj, &tmpreg, irnode[i].Op, sbt, stack, vreg, &ret)
						continue
					case ir.ImmFloat: //op<tmp,immfloat,stackvar> i!=0
						OpTmpAndImmFloatDestStackVar(to, irnode[i].ResultObj, irnode[i].Arg2Obj, &tmpreg, irnode[i].Op, stack, vreg, &ret)
						continue
					}
				case ir.ImmInt:
					switch irnode[i].Arg2Typ {
					case ir.Tmp: //op<immint,tmp,stackvar> i!=0 ?
						OpTmpAndImmIntDestStackVar(irnode[i].ResultObj, irnode[i].Arg1Obj, &tmpreg, irnode[i].Op, stack, vreg, &ret)
						continue
					}
				}
			}
		}
		panic(fmt.Errorf("i=%d v=%+v", i, irnode))
	}
	retstruct := NewAssignKind(ret)
	return retstruct
}
